﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.Remoting.Proxies;
using System.Runtime.Remoting.Messaging;
using System.Reflection;
using System.Collections.Specialized;

namespace Normal
{
    /// <summary>
    /// This class is a proxy to intercept fields and method calls
    /// in order to implement the "On Demmand" SQL calls
    /// </summary>
    sealed class NormalFKProxy : RealProxy
    {
        private object _instance;
        private object _id;
        private string _connectionString;
        private string _providerName;
        private bool _empty;

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="obj">Object Instance To "Proxy"</param>
        /// <param name="type">Type of the Instance</param>
        private NormalFKProxy(object instance, Type type, object id, string ConnectionString,
            string ProviderName)
            : base(type)
        {
            this._instance = instance;
            this._id = id;
            this._empty = true;
            this._providerName = ProviderName;
            this._connectionString = ConnectionString;
        }

        /// <summary>
        /// This override method is called whenever each property or method
        /// is called
        /// </summary>
        /// <param name="msg">The Proxy Message</param>
        /// <returns>The Message to be processed</returns>
        public override IMessage Invoke(IMessage msg)
        {
            MethodCallMessageWrapper mc = new MethodCallMessageWrapper((IMethodCallMessage)msg);

            MethodInfo method = mc.MethodBase as MethodInfo;

            if (_empty && (method.Name.StartsWith("get_") || method.Name.StartsWith("set_")))
            {
                DAO<object> dao = new DAO<object>(_connectionString, _providerName);
                object newObj = dao.FindById(new object[] { _id }, _instance.GetType(), null);
                if (newObj != null)
                {
                    _instance = newObj;
                }
                _empty = false;
            }

            object res = method.Invoke(
                this._instance
              , ((method.Attributes & MethodAttributes.SpecialName) == MethodAttributes.SpecialName) ? mc.InArgs : mc.Args);

            return new ReturnMessage(res, mc.Args, mc.Args.Length, mc.LogicalCallContext, mc);
        }

        /// <summary>
        /// Creates a Proxy Instance based on a given instance
        /// </summary>
        /// <param name="instance">Instance to be passed</param>
        /// <returns>The "Proxied" instance</returns>
        public static object CreateProxy(object instance, object id, string ConnectionString,
            string ProviderName)
        {
            //Only works if the object inherits from MarshalByRefObject
            //Otherwise will return the class that won't be proxied
            if (instance != null && instance is MarshalByRefObject)
            {
                return new NormalFKProxy(instance, instance.GetType(), id, ConnectionString,
                    ProviderName).GetTransparentProxy();
            }
            return instance;
        }
    }
}
